/* Vecmath.

   Copyright (C) 2001, 2002, 2003 Stefan Maierhofer.

   Written by Stefan Maierhofer <sm@cg.tuwien.ac.at>

   This file is part of Vecmath.

   Vecmath is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   Vecmath is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Vecmath; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */

using System;
using NUnit.Framework;

namespace Vecmath
{

    /// <summary>
    /// Trafo2D.
    /// </summary>
    public struct Trafo2D
    {

        public static Trafo2D Identity = new Trafo2D(
            1,0,0, 0,1,0, 0,0,1
            );

        public Trafo2D(
            double x00, double x01, double x02,
            double x10, double x11, double x12,
            double x20, double x21, double x22
            )
        {
            _x = new double[9];
            _x[ 0] = x00; _x[ 1] = x01; _x[ 2] = x02;
            _x[ 3] = x10; _x[ 4] = x11; _x[ 5] = x12;
            _x[ 6] = x20; _x[ 7] = x21; _x[ 8] = x22;
        }

        public Trafo2D(Rot2D r)
        {
            this = r.ToTrafo2D();
        }

        public double this [int i, int j]
        {
            get { return _x[i*3+j]; }
            set { _x[i*3+j] = value; }
        }

        public Trafo2D Transposed
        {
            get
            {
                return new Trafo2D(
                    _x[0], _x[3], _x[6],
                    _x[1], _x[4], _x[7],
                    _x[2], _x[5], _x[8]
                    );
            }
        }

        public void Transpose()
        {
            this = this.Transposed;
        }

        public double Det
        {
            get
            {
                return
                    _x[0] * _x[4] * _x[8] + _x[1] * _x[5] * _x[6] + _x[2] * _x[3] * _x[7]
                  - _x[6] * _x[4] * _x[2] - _x[7] * _x[5] * _x[0] - _x[8] * _x[3] * _x[1];
            }
        }

        public Trafo2D Adjoint
        {
            get
            {
                return new Trafo2D(
                    Vecmath.Det2x2(
                        _x[ 4], _x[ 5],
                        _x[ 7], _x[ 8]), // 00
                    - Vecmath.Det2x2(
                        _x[ 3], _x[ 5],
                        _x[ 6], _x[ 8]), // 01
                    Vecmath.Det2x2(
                        _x[ 3], _x[ 4],
                        _x[ 6], _x[ 7]), // 02

                    - Vecmath.Det2x2(
                        _x[ 1], _x[ 2],
                        _x[ 7], _x[ 8]), // 10
                    Vecmath.Det2x2(
                        _x[ 0], _x[ 2],
                        _x[ 6], _x[ 8]), // 11
                    - Vecmath.Det2x2(
                        _x[ 0], _x[ 1],
                        _x[ 6], _x[ 7]), // 12

                    Vecmath.Det2x2(
                        _x[ 1], _x[ 2],
                        _x[ 4], _x[ 5]), // 20
                    - Vecmath.Det2x2(
                        _x[ 0], _x[ 2],
                        _x[ 3], _x[ 5]), // 21
                    Vecmath.Det2x2(
                        _x[ 0], _x[ 1],
                        _x[ 3], _x[ 4])  // 22
                    
                    );
            }
        }

        public Trafo2D Inverse
        {
            get
            {
                double d = Det;
                if (d == 0) throw new ArithmeticException("Singular Matrix");
                d = 1.0 / d;
                return Adjoint.Transposed * d;
            }
        }

        public void Invert()
        {
            this = Inverse;
        }

        public static Trafo2D operator+(Trafo2D t, double f)
        {
            return new Trafo2D(
                t._x[ 0] + f, t._x[ 1] + f, t._x[ 2] + f,
                t._x[ 3] + f, t._x[ 4] + f, t._x[ 5] + f,
                t._x[ 6] + f, t._x[ 7] + f, t._x[ 8] + f
                );
        }

        public static Trafo2D operator-(Trafo2D t, double f)
        {
            return new Trafo2D(
                t._x[ 0] - f, t._x[ 1] - f, t._x[ 2] - f,
                t._x[ 3] - f, t._x[ 4] - f, t._x[ 5] - f,
                t._x[ 6] - f, t._x[ 7] - f, t._x[ 8] - f
                );
        }

        public static Trafo2D operator*(Trafo2D t, double f)
        {
            return new Trafo2D(
                t._x[ 0] * f, t._x[ 1] * f, t._x[ 2] * f,
                t._x[ 3] * f, t._x[ 4] * f, t._x[ 5] * f,
                t._x[ 6] * f, t._x[ 7] * f, t._x[ 8] * f
                );
        }

        public static Trafo2D operator/(Trafo2D t, double f)
        {
            return new Trafo2D(
                t._x[ 0] / f, t._x[ 1] / f, t._x[ 2] / f,
                t._x[ 3] / f, t._x[ 4] / f, t._x[ 5] / f,
                t._x[ 6] / f, t._x[ 7] / f, t._x[ 8] / f
                );
        }


        public static Pnt2D operator*(Pnt2D p, Trafo2D t)
        {
            double f = 1.0 / (p[0]*t._x[2]+p[1]*t._x[5]+t._x[8]);
            return new Pnt2D(
                (p[0] * t._x[0] + p[1] * t._x[3] + t._x[6]) * f,
                (p[0] * t._x[1] + p[1] * t._x[4] + t._x[7]) * f
                );
        }

        public static Vec2D operator*(Vec2D v, Trafo2D t)
        {
            return new Vec2D(
                v[0] * t._x[0] + v[1] * t._x[3],
                v[0] * t._x[1] + v[1] * t._x[4]
                );
        }

        public static Box2D operator*(Box2D b, Trafo2D t)
        {
            Box2D result = Box2D.Empty;
            result += b.LL * t;
            result += b.LU * t;
            result += b.UL * t;
            result += b.UU * t;
            return result;
        }

        public static Trafo2D operator*(Trafo2D a, Trafo2D b)
        {
            return new Trafo2D(

                a._x[ 0]*b._x[ 0]+a._x[ 1]*b._x[ 3]+a._x[ 2]*b._x[ 6],
                a._x[ 0]*b._x[ 1]+a._x[ 1]*b._x[ 4]+a._x[ 2]*b._x[ 7],
                a._x[ 0]*b._x[ 2]+a._x[ 1]*b._x[ 5]+a._x[ 2]*b._x[ 8],

                a._x[ 3]*b._x[ 0]+a._x[ 4]*b._x[ 3]+a._x[ 5]*b._x[ 6],
                a._x[ 3]*b._x[ 1]+a._x[ 4]*b._x[ 4]+a._x[ 5]*b._x[ 7],
                a._x[ 3]*b._x[ 2]+a._x[ 4]*b._x[ 5]+a._x[ 5]*b._x[ 8],

                a._x[ 6]*b._x[ 0]+a._x[ 7]*b._x[ 3]+a._x[ 8]*b._x[ 6],
                a._x[ 6]*b._x[ 1]+a._x[ 7]*b._x[ 4]+a._x[ 8]*b._x[ 7],
                a._x[ 6]*b._x[ 2]+a._x[ 7]*b._x[ 5]+a._x[ 8]*b._x[ 8]

                );
        }

        internal double [] _x;

        public double [] Array { get { return _x; } }

    }


 
}
